package org.mathcloud.math.io.mml.content.generic;

import java.util.ArrayList;
import java.util.List;

import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.Text;
import org.mathcloud.math.core.common.factory.QuantityObjectFactory;
import org.mathcloud.math.core.common.quantity.ComplexNumber;
import org.mathcloud.math.core.common.quantity.QuantityObject;
import org.mathcloud.math.core.common.quantity.RealNumber;
import org.mathcloud.math.core.generic.signature.GenericQuantityObjectSignature;
import org.mathcloud.math.io.mml.content.common.CNElementTranslator;

/* TO DO List
 * 1. add translation of different types of CN elements
 * */

public class GenericCNElementTranslator extends GenericElementTranslator
implements CNElementTranslator
{
	public GenericCNElementTranslator()
	{
		super("cn");
		resultObject = null;
		initTypes4RealNumber();
		initTypes4ComplexNumber();
	}
	
	public GenericCNElementTranslator(Element in)
	{
		super("cn");
		resultObject = null;
		setRequestElement(in);
		initTypes4RealNumber();
		initTypes4ComplexNumber();
	}

	private void initTypes4RealNumber()
	{
		/* initialization of list of types allowed for CN element 
		 * representing real number
		 * */
		types4RealNumber = new ArrayList<String>();
		types4RealNumber.add("integer");
		types4RealNumber.add("real");
		types4RealNumber.add("double");
		types4RealNumber.add("hexdouble");
		types4RealNumber.add("e-notation");
		types4RealNumber.add("rational");		
	}
	private void initTypes4ComplexNumber()
	{
		/* initialization of list of types allowed for CN element 
		 * representing complex number
		 * */
		types4ComplexNumber = new ArrayList<String>();
		types4ComplexNumber.add("complex-cartesian");
		types4ComplexNumber.add("complex-polar");
	}
	
	@Override
	public void translate()
	{
		/* We propose that correctness of elements and attributes is verified 
		 * by matching to XML Schema
		*/
		if ((requestElement!=null)&&(factory!=null))
		{			
			if (requestElement.getAttribute("type")==null)
				resultObject = processRealNumberCN(requestElement,factory);
			else if (types4RealNumber.contains(requestElement.getAttribute("type").getValue()))					
				resultObject = processRealNumberCN(requestElement,factory);				
			else if (types4ComplexNumber.contains(requestElement.getAttribute("type").getValue()))
				resultObject = processComplexNumberCN(requestElement, factory);
			if (resultObject==null)
				resultElement = getCErrorElement("CN Element translation failed");
			else resultElement = (Element)requestElement.clone();
		}
	}

	@Override
	public boolean validateRequest()
	{
		return false;
	}
	
	protected RealNumber processRealNumberCN(Element arg0,QuantityObjectFactory factory)
	{
		/* getting RealNumber object from CN element
		 * */
		GenericQuantityObjectSignature sign = (GenericQuantityObjectSignature) GenericQuantityObjectSignature.getRealNumberSignature();
		if (arg0.getAttribute("type")==null)
		{
			RealNumber res = (RealNumber) factory.newInstance(sign);
			if (res!=null)
				res.setValue(Double.valueOf(requestElement.getText()));
			return res;
		} 
		else if (arg0.getAttribute("type").getValue().equalsIgnoreCase("double"))
		{
			RealNumber res = (RealNumber) factory.newInstance(sign);
			if (res!=null)
				res.setValue(Double.valueOf(requestElement.getText()));			
			return res;			
		}
		else return null;
	}
	
	protected ComplexNumber processComplexNumberCN(Element arg0, QuantityObjectFactory factory)
	{
		/* getting ComplexNumber object from CN element
		 * */
		GenericQuantityObjectSignature sign = (GenericQuantityObjectSignature) GenericQuantityObjectSignature.getRealNumberSignature();
		sign.setComplex(true);
		ComplexNumber res = (ComplexNumber) factory.newInstance(sign);
		if (res!=null)
		{
			if (arg0.getAttribute("type").getValue().equalsIgnoreCase("complex-cartesian"))
			{
				res.setRealPart(Double.valueOf(((Text)requestElement.getContent().get(0)).getText()));
				res.setImagePart(Double.valueOf(((Text)requestElement.getContent().get(2)).getText()));			
			}
			else 
			{
				res.setMod(Double.valueOf(((Text)requestElement.getContent().get(0)).getText()));
				res.setArg(Double.valueOf(((Text)requestElement.getContent().get(2)).getText()));						
			}			
		}
		return res;
	}	
	
	@Override	
	public Element getMathMLContent(QuantityObject in, String type)
	{
		/* get CN element from RealNumber or ComplexNumber
		 * */
		if (in!=null)
		{
			if (in.getSignature().isRealNumberSignature())
			{
				if (type!=null)
					return getMMLContent((RealNumber)in, type);
				else return getMMLContent((RealNumber)in,"double");
			}				
			else if (in.getSignature().isComplexNumberSignature())
			{
				if (type!=null)
					return getMMLContent((ComplexNumber)in, type);
				else return getMMLContent((ComplexNumber)in, "complex-cartesian");
			}				
			else return null;			
		}
		else return null;
	}
	
  public Element getMMLContent (RealNumber in, String type)
  {
  	Element out = new Element("cn", Namespace.getNamespace(prefix,namespaceURI));
  	if (types4RealNumber.contains(type))
  	{
    	out.setAttribute("type",type);
      out.addContent(String.valueOf(in.getValue()));
      return out;    	    		    		
  	}
  	else 
  	{
    	out.setAttribute("type","double");
      out.addContent(String.valueOf(in.getValue()));
      return out;    	    		
  	}
  }

  public Element getMMLContent(ComplexNumber in, String type)
  {
    /* type variable defines what type of CN element should be used
     * - complex-cartesian or
     * - complex-polar
     * */
		Element out = new Element("cn", Namespace.getNamespace(prefix,namespaceURI));
		String t;
		if (type==null)
			t = "complex-cartesian";
		else if (type.equalsIgnoreCase("complex-cartesian")||type.equalsIgnoreCase("complex-polar"))
			t = type;
		else t = "complex-cartesian";
		out.setAttribute("type",t);
		if (t.equalsIgnoreCase("complex-cartesian"))
  	{
  		out.addContent(String.valueOf(in.getRealPart()));
      out.addContent(new Element("sep",out.getNamespace()));
      out.addContent(String.valueOf(in.getImagePart()));    		
      return out;
  	}
  	else if (type.equalsIgnoreCase("complex-polar"))
  	{
      out.addContent(String.valueOf(in.getMod()));
      out.addContent(new Element("sep",out.getNamespace()));
      out.addContent(String.valueOf(in.getArg()));
      return out;
  	}
  	else return null;    	  	
  }
	
  @Override
	public QuantityObject getResultObject()
	{
		return resultObject;
	}
  
	@Override
	public QuantityObjectFactory getFactory()
	{
		return factory;
	}

	@Override
	public void setFactory(QuantityObjectFactory arg0)
	{
		factory = arg0;
	}

	private List<String> types4RealNumber;
	private List<String> types4ComplexNumber;
	private QuantityObject resultObject;
	private QuantityObjectFactory factory;	
}
